// Created by Roman Novitsky on 19.05.2009


#region Imported Namespaces


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Windows.Data;

using WtfManager.Framework;


#endregion


namespace WtfManager.Utilities
{
  public class ConverterService: ExtensibleService, IValueConverter
  {
    #region Fields


    private static ConverterService _instatce;

    private readonly TypeKeyDictionary<TypeKeyDictionary<IValueConverter>> _converters =
      new TypeKeyDictionary<TypeKeyDictionary<IValueConverter>>();


    #endregion


    #region Constructors/Finalizers


    internal ConverterService()
    {
    }


    #endregion


    #region Properties/Indexers


    public override string Description
    {
      get
      {
        return "Converts data to different types.";
      }
    }


    public static ConverterService Instatce
    {
      get
      {
        return _instatce;
      }
    }

    public override string Name
    {
      get
      {
        return "Convertor Service";
      }
    }


    #endregion


    #region Methods


    protected override void OnStateChnaged()
    {
      _instatce = State == FrameworkComponentState.Initialized ? this : null;
    }


    protected override void OnUnload()
    {
    }


    protected override FrameworkComponentState OnInitialize()
    {
      return FrameworkComponentState.Initialized;
    }


    protected override FrameworkComponentState OnLoad()
    {
      return FrameworkComponentState.Loaded;
    }


    protected override void OnExtensionAdded(Service extension)
    {
      var converter = (IValueConverter)extension;
      List<ValueConversionAttribute> attributes = GetValueConversionAttributes(converter);

      foreach (ValueConversionAttribute attribute in attributes)
      {
        AddConverter(attribute.SourceType, attribute.TargetType, converter);
      }
    }


    private static List<ValueConversionAttribute> GetValueConversionAttributes(IValueConverter converter)
    {
      Type type = converter.GetType();
      object[] attributes = type.GetCustomAttributes(typeof(ValueConversionAttribute), true);
      var valueConversionAttributes = new List<ValueConversionAttribute>();

      foreach (object attribute in attributes)
      {
        valueConversionAttributes.Add((ValueConversionAttribute)attribute);
      }

      return valueConversionAttributes;
    }


    private object Convert(object value, Type targetType, object parameter, CultureInfo culture, bool convertBack)
    {
      Type sourceType = value.GetType();

      List<Type> suitableTypes = _converters.GetSuitableTypes(sourceType);

      foreach (Type suitableType in suitableTypes)
      {
        TypeKeyDictionary<IValueConverter> converters = _converters[suitableType];
        IValueConverter converter = converters.GetBestValue(targetType);

        try
        {
          object convertedValue = convertBack
                                    ? converter.ConvertBack(value, targetType, parameter, culture)
                                    : converter.Convert(value, targetType, parameter, culture);

          if (convertedValue != null)
          {
            return convertedValue;
          }
        }
        catch
        {
          continue;
        }
      }

      return null;
    }


    private void AddConverter(Type sourceType, Type targetType, IValueConverter converter)
    {
      AddConverterToDictionary(sourceType, targetType, converter);
      AddConverterToDictionary(targetType, sourceType, converter);
    }


    private void AddConverterToDictionary(Type sourceType, Type targetType, IValueConverter converter)
    {
      if (!_converters.ContainsKey(sourceType))
      {
        _converters.Add(sourceType, new TypeKeyDictionary<IValueConverter>());
      }

      Dictionary<Type, IValueConverter> sourceTypeConverters = _converters[sourceType];

      if (sourceTypeConverters.ContainsKey(targetType))
      {
        Debug.Fail("A converter for such types was already added.");
        return;
      }

      sourceTypeConverters.Add(targetType, converter);
    }


    private void RemoveConverter(Type sourceType, Type targetType)
    {
      RemoveConverterFromDictionary(sourceType, targetType);
      RemoveConverterFromDictionary(targetType, sourceType);
    }


    private void RemoveConverterFromDictionary(Type sourceType, Type targetType)
    {
      if (!_converters.ContainsKey(sourceType))
      {
        Debug.Fail("There is no converter for specified type.");
        return;
      }

      Dictionary<Type, IValueConverter> sourceTypeConverters = _converters[sourceType];

      if (!sourceTypeConverters.ContainsKey(targetType))
      {
        Debug.Fail("There is no converter for specified type.");
        return;
      }

      sourceTypeConverters.Remove(targetType);
    }


    public override bool IsExtension(Service extension)
    {
      return extension is IValueConverter;
    }


    protected override void OnExtensionRemoved(Service extension)
    {
      var converter = (IValueConverter)extension;
      List<ValueConversionAttribute> attributes = GetValueConversionAttributes(converter);

      foreach (ValueConversionAttribute attribute in attributes)
      {
        RemoveConverter(attribute.SourceType, attribute.TargetType);
      }
    }


    public object Convert(object item, Type targetType)
    {
      return Convert(item, targetType, null, CultureInfo.InvariantCulture);
    }


    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return Convert(value, targetType, parameter, culture, false);
    }


    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
      return Convert(value, targetType, parameter, culture, true);
    }


    #endregion
  }
}